﻿using System;
using Adalbertus.BudgetPlanner.Domain;
using Adalbertus.BudgetPlanner.Domain.Repositories;

namespace Adalbertus.BudgetPlanner.Services
{
    public class SavingService : BaseService<Saving>, ISavingService
    {
        private const string SAVING_CASH_FLOW_NAME = "Oszczędności";

        public SavingService(IRepositoryFactory repositoryFactory, IUnitOfWorkProvider unitOfWorkProvider)
            : base(repositoryFactory, unitOfWorkProvider)
        {
        }

        public Saving Create(string name)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                Saving saving = new Saving { Name = name };
                CashFlow savingFlow = CreateSavingFlow(saving);

                IRepository<Saving> savingRepository = RepositoryFactory.CreateRepository<Saving>(unitOfWork);
                IRepository<CashFlow> cashFlowRepository = RepositoryFactory.CreateRepository<CashFlow>(unitOfWork);

                savingRepository.Save(saving);
                cashFlowRepository.Save(savingFlow);

                unitOfWork.Commit();
                return saving;
            }
        }

        public override void Save(Saving saving)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<Saving> savingRepository = RepositoryFactory.CreateRepository<Saving>(unitOfWork);
                savingRepository.Save(saving);

                ICashFlowRepository cashFlowRepository = RepositoryFactory.CreateCashFlowRepository(unitOfWork);
                CashFlow savingFlow = cashFlowRepository.GetBySaving(saving);
                if (savingFlow != null)
                {
                    savingFlow.Name = saving.Name;
                }
                else
                {
                    savingFlow = CreateSavingFlow(saving);
                }
                cashFlowRepository.Save(savingFlow);

                unitOfWork.Commit();
            }
        }

        public SavingEntry CreateEntry(Saving saving, decimal value, DateTime date)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<Saving> savingRepository = RepositoryFactory.CreateRepository<Saving>(unitOfWork);

                SavingEntry savingEntry = new SavingEntry
                                              {
                                                  Date = date,
                                                  Value = value,
                                                  Saving = saving
                                              };

                saving.Entries.Add(savingEntry);
                savingRepository.Save(saving);
                unitOfWork.Commit();

                return savingEntry;
            }
        }

        public override void Remove(Saving saving)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<Saving> savingRepository = RepositoryFactory.CreateRepository<Saving>(unitOfWork);
                savingRepository.Remove(saving);

                ICashFlowRepository cashFlowRepository = RepositoryFactory.CreateCashFlowRepository(unitOfWork);
                CashFlow savingFlow = cashFlowRepository.GetBySaving(saving);
                if (savingFlow != null)
                {
                    cashFlowRepository.Remove(savingFlow);
                }
                unitOfWork.Commit();
            }
        }

        public void RemoveEntry(SavingEntry entry)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                entry.Saving.Entries.Remove(entry);

                IRepository<Saving> savingRepository = RepositoryFactory.CreateRepository<Saving>(unitOfWork);

                savingRepository.Save(entry.Saving);

                unitOfWork.Commit();
            }
        }

        private CashFlow CreateSavingFlow(Saving saving)
        {
            return new CashFlow
            {
                Name = saving.Name,
                Description = SAVING_CASH_FLOW_NAME,
                Saving = saving
            };
        }
    }
}
